/*
 * 11/19/04 1.0 moved to LGPL.
 * 16/11/99 Renamed class, added javadoc, and changed table
 * name from String to 3 chars. mdm@techie.com
 * 02/15/99 Java Conversion by E.B, javalayer@javazoom.net
 *
 * 04/19/97 : Adapted from the ISO MPEG Audio Subgroup Software Simulation
 *  Group's public c source for its MPEG audio decoder. Miscellaneous
 *  changes by Jeff Tsay (ctsay@pasteur.eecs.berkeley.edu).
 *-----------------------------------------------------------------------
 * Copyright (c) 1991 MPEG/audio software simulation group, All Rights Reserved
 * MPEG/audio coding/decoding software, work in progress              
 *   NOT for public distribution until verified and approved by the   
 *   MPEG/audio committee.  For further information, please contact   
 *   Davis Pan, 508-493-2241, e-mail: pan@3d.enet.dec.com             
 *                                                                    
 * VERSION 4.1                                                        
 *   changes made since last update:                                  
 *   date   programmers         comment                        
 *  27.2.92 F.O.Witte (ITT Intermetall)
 *  8/24/93 M. Iwadare          Changed for 1 pass decoding.          
 *  7/14/94 J. Koller useless 'typedef' before huffcodetab  removed
 *-----------------------------------------------------------------------
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------
 */
package org.mp3transform;

final class Huffman {
    private static final int MX_OFF = 250;
    private static final int HUFFMAN_COUNT = 34;
    public static final Huffman[] HUFFMAN;
    private final char tableName0;
    private final char tableName1;
    private final int xlen; // max. x-index+
    private final int ylen; // max. y-index+
    private final int linbits; // number of linbits
    private final int[] val0; // decoder tree
    private final int[] val1; // decoder tree
    private final int treeLen; // length of decoder tree

    private Huffman(String name, int xlen, int ylen, int linbits, int[][] val,
            int treeLen) {
        tableName0 = name.charAt(0);
        tableName1 = name.charAt(1);
        this.xlen = xlen;
        this.ylen = ylen;
        this.linbits = linbits;
        val0 = new int[val.length];
        val1 = new int[val.length];
        for (int i = 0; i < val.length; i++) {
            val0[i] = val[i][0];
            val1[i] = val[i][1];
        }
        this.treeLen = treeLen;
    }

    /**
     * Do the huffman-decoding.
     * For counta, countb - the 4 bit value is
     * returned in y, discard x.
     */
    public int decode(Layer3Decoder decoder, BitReservoir br) {
        // array of all huffcodtable headers
        // 0..31 Huffman code table 0..31
        // 32,33 count1-tables

        // table 0 needs no bits
        if (treeLen == 0) {
            decoder.x = decoder.y = 0;
            return 0;
        }
        int level = 1 << ((4 * 8) - 1);
        int point = 0;
        int error = 1;
        // Lookup in Huffman table
        do {
            if (val0[point] == 0) {
                // end of tree
                decoder.x = val1[point] >>> 4;
                decoder.y = val1[point] & 0xf;
                error = 0;
                break;
            }
            if (br.getOneBit() != 0) {
                while (val1[point] >= MX_OFF) {
                    point += val1[point];
                }
                point += val1[point];
            } else {
                while (val0[point] >= MX_OFF) {
                    point += val0[point];
                }
                point += val0[point];
            }
            level >>>= 1;
        } while (level != 0);
        // Process sign encodings for quadruples tables
        if (tableName0 == '3' && (tableName1 == '2' || tableName1 == '3')) {
            decoder.v = (decoder.y >> 3) & 1;
            decoder.w = (decoder.y >> 2) & 1;
            decoder.x = (decoder.y >> 1) & 1;
            decoder.y = decoder.y & 1;
            // v, w, x and y are reversed in the bitstream
            // switch them around to make test bistream work.
            if (decoder.v != 0) {
                if (br.getOneBit() != 0) {
                    decoder.v = -decoder.v;
                }
            }
            if (decoder.w != 0) {
                if (br.getOneBit() != 0) {
                    decoder.w = -decoder.w;
                }
            }
            if (decoder.x != 0) {
                if (br.getOneBit() != 0) {
                    decoder.x = -decoder.x;
                }
            }
            if (decoder.y != 0) {
                if (br.getOneBit() != 0) {
                    decoder.y = -decoder.y;
                }
            }
        } else {
            // Process sign and escape encodings for dual tables.
            // x and y are reversed in the test bitstream.
            // Reverse x and y here to make test bitstream work.
            if (linbits != 0) {
                if ((xlen - 1) == decoder.x) {
                    decoder.x += br.getBits(linbits);
                }
            }
            if (decoder.x != 0) {
                if (br.getOneBit() != 0) {
                    decoder.x = -decoder.x;
                }
            }
            if (linbits != 0) {
                if ((ylen - 1) == decoder.y) {
                    decoder.y += br.getBits(linbits);
                }
            }
            if (decoder.y != 0) {
                if (br.getOneBit() != 0) {
                    decoder.y = -decoder.y;
                }
            }
        }
        return error;
    }

    static {
        HUFFMAN = new Huffman[HUFFMAN_COUNT];
        HUFFMAN[0] = new Huffman("0 ", 0, 0, 0, Constants.VAL_TAB_0, 0);
        HUFFMAN[1] = new Huffman("1 ", 2, 2, 0, Constants.VAL_TAB_1, 7);
        HUFFMAN[2] = new Huffman("2 ", 3, 3, 0, Constants.VAL_TAB_2, 17);
        HUFFMAN[3] = new Huffman("3 ", 3, 3, 0, Constants.VAL_TAB_3, 17);
        HUFFMAN[4] = new Huffman("4 ", 0, 0, 0, Constants.VAL_TAB_4, 0);
        HUFFMAN[5] = new Huffman("5 ", 4, 4, 0, Constants.VAL_TAB_5, 31);
        HUFFMAN[6] = new Huffman("6 ", 4, 4, 0, Constants.VAL_TAB_6, 31);
        HUFFMAN[7] = new Huffman("7 ", 6, 6, 0, Constants.VAL_TAB_7, 71);
        HUFFMAN[8] = new Huffman("8 ", 6, 6, 0, Constants.VAL_TAB_8, 71);
        HUFFMAN[9] = new Huffman("9 ", 6, 6, 0, Constants.VAL_TAB_9, 71);
        HUFFMAN[10] = new Huffman("10", 8, 8, 0, Constants.VAL_TAB_10, 127);
        HUFFMAN[11] = new Huffman("11", 8, 8, 0, Constants.VAL_TAB_11, 127);
        HUFFMAN[12] = new Huffman("12", 8, 8, 0, Constants.VAL_TAB_12, 127);
        HUFFMAN[13] = new Huffman("13", 16, 16, 0, Constants.VAL_TAB_13, 511);
        HUFFMAN[14] = new Huffman("14", 0, 0, 0, Constants.VAL_TAB_14, 0);
        HUFFMAN[15] = new Huffman("15", 16, 16, 0, Constants.VAL_TAB_15, 511);
        HUFFMAN[16] = new Huffman("16", 16, 16, 1, Constants.VAL_TAB_16, 511);
        HUFFMAN[17] = new Huffman("17", 16, 16, 2, Constants.VAL_TAB_16, 511);
        HUFFMAN[18] = new Huffman("18", 16, 16, 3, Constants.VAL_TAB_16, 511);
        HUFFMAN[19] = new Huffman("19", 16, 16, 4, Constants.VAL_TAB_16, 511);
        HUFFMAN[20] = new Huffman("20", 16, 16, 6, Constants.VAL_TAB_16, 511);
        HUFFMAN[21] = new Huffman("21", 16, 16, 8, Constants.VAL_TAB_16, 511);
        HUFFMAN[22] = new Huffman("22", 16, 16, 10, Constants.VAL_TAB_16, 511);
        HUFFMAN[23] = new Huffman("23", 16, 16, 13, Constants.VAL_TAB_16, 511);
        HUFFMAN[24] = new Huffman("24", 16, 16, 4, Constants.VAL_TAB_24, 512);
        HUFFMAN[25] = new Huffman("25", 16, 16, 5, Constants.VAL_TAB_24, 512);
        HUFFMAN[26] = new Huffman("26", 16, 16, 6, Constants.VAL_TAB_24, 512);
        HUFFMAN[27] = new Huffman("27", 16, 16, 7, Constants.VAL_TAB_24, 512);
        HUFFMAN[28] = new Huffman("28", 16, 16, 8, Constants.VAL_TAB_24, 512);
        HUFFMAN[29] = new Huffman("29", 16, 16, 9, Constants.VAL_TAB_24, 512);
        HUFFMAN[30] = new Huffman("30", 16, 16, 11, Constants.VAL_TAB_24, 512);
        HUFFMAN[31] = new Huffman("31", 16, 16, 13, Constants.VAL_TAB_24, 512);
        HUFFMAN[32] = new Huffman("32", 1, 16, 0, Constants.VAL_TAB_32, 31);
        HUFFMAN[33] = new Huffman("33", 1, 16, 0, Constants.VAL_TAB_33, 31);
    }
}
